﻿//--------------------------------------------------------------------------
// 
//  Copyright (c) Chili Software.  All rights reserved. 
// 
//  File: XmlTypeMappingProvider.cs
//
//  Description: Creates the mapping for a persistent type by using
//               XML files as a mapping source.
// 
//--------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using Chili.Opf3.Relations;

namespace Chili.Opf3.Mapping.Providers
{
	/// <summary>
	/// Creates the type mapping for one or more persistent types by using XML data 
	/// (contained in XML files or embedded stream) that hold the mapping for the persistent objects.
	/// </summary>
	/// <remarks>
	/// For additional information on how to compose the XML for the mapping file please consult 
	/// the documentation of the <see cref="XmlMapping" /> class.
	/// </remarks>
	/// <example>
	/// The following example shows how to register the mapping provider with the framework.
	/// <code lang="csharp">
	/// // Create an instance of the XML type mapping provider.
	/// XmlTypeMappingProvider provider = new XmlTypeMappingProvider();
	/// // Load an XML mapping from a given file and add it to the provider.
	/// provider.AddXmlMapping(XmlMapping.Load("Mapping.xml"));
	///
	/// // Add this provider as mapping provider for the framework.
	/// // To remove all other registered providers (by default the attribute based
	/// // mapping provider is registered) call RemoveAll before adding the XML
	/// // based mapping provider.
	/// TypeMapping.Providers.Add(provider);
	/// </code>
	/// </example>
	public sealed class XmlTypeMappingProvider : ITypeMappingProvider
	{
		private List<XmlMapping> _mappings = new List<XmlMapping>();

		/// <summary>
		/// Returns the persistent attribute of the type or null if the type has no 
		/// <see cref="PersistentAttribute">PersistentAttribute</see>.
		/// </summary>
		/// <param name="type">Type investigated for the <see cref="PersistentAttribute">PersistentAttribute</see>.</param>
		public PersistentAttribute GetPersistentAttribute(Type type)
		{
			if (type == null)
				throw new ArgumentNullException("type");

			// loop over all mappings that are registered with this instance.
			foreach (XmlMapping mapping in _mappings)
			{
				// if the mapping returns a valid attribute return it.
				PersistentAttribute attribute = mapping.GetPersistentAttribute(type);
				if (attribute != null)
					return attribute;
			}

			return null;
		}

		/// <summary>
		/// Returns the <see cref="FieldAttribute">FieldAttribute</see> for the given member. If the member is not mapped,
		/// null is returned.
		/// </summary>
		/// <param name="type">The persistent type that's mapping is created.</param>
		/// <param name="member">The member of the persistent type.</param>
		/// <returns>An instance of the <see cref="FieldAttribute">FieldAttribute</see> or null if the member is not mapped.</returns>
		public FieldAttribute GetMemberFieldAttribute(Type type, MemberInfo member)
		{
			if (type == null)
				throw new ArgumentNullException("type");
			if (member == null)
				throw new ArgumentNullException("member");

			// loop over all mappings that are registered with this instance.
			foreach (XmlMapping mapping in _mappings)
			{
				// if the mapping returns a valid attribute return it.
				FieldAttribute attribute = mapping.GetFieldAttribute(type, member);
				if (attribute != null)
					return attribute;
			}

			return null;
		}

		/// <summary>
		/// Returns the <see cref="RelationAttribute">RelationAttribute</see> for the given member. If the member does not
		/// have an attribute set, null is returned.
		/// </summary>
		/// <param name="type">The persistent type that's mapping is created.</param>
		/// <param name="member">The member of the persistent type.</param>
		/// <param name="relatedType">The related object's type.</param>
		/// <returns>An instance of the <see cref="RelationAttribute">RelationAttribute</see> or null if the member does not have
		/// an attribute set.</returns>
		public RelationAttribute GetMemberRelationAttribute(Type type, MemberInfo member, Type relatedType)
		{
			if (type == null)
				throw new ArgumentNullException("type");
			if (member == null)
				throw new ArgumentNullException("member");
			if (relatedType == null)
				throw new ArgumentNullException("relatedType");

			// loop over all mappings that are registered with this instance.
			foreach (XmlMapping mapping in _mappings)
			{
				// if the mapping returns a valid attribute return it.
				RelationAttribute attribute = mapping.GetRelationAttribute(type, member, relatedType);
				if (attribute != null)
					return attribute;
			}

			return null;
		}

		/// <summary>
		/// Adds an <see cref="XmlMapping" /> to the provider.
		/// </summary>
		/// <param name="mapping">The <see cref="XmlMapping" /> that's added to the provider.</param>
		public void AddXmlMapping(XmlMapping mapping)
		{
			if (mapping == null)
				throw new ArgumentNullException("mapping");

			_mappings.Add(mapping);
		}
	}
}